﻿using NiklasKarl.WarpCam;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Storage.Streams;
using Nokia.Graphics.Imaging;

namespace NiklasKarl.WarpCam.Effects
{
    public class WarpHandler : ICameraEffect
    {
        private static readonly Guid LevelPropertyGuid = new Guid("{9C0C6495-C02D-4E77-97B6-911D7A5B6DF2}");

        private static readonly List<CameraHandlerProperty> WarpHandlerProperties = new List<CameraHandlerProperty>(
            new CameraHandlerProperty[] { 
                new CameraHandlerSliderProperty("Level", LevelPropertyGuid, 0, 10)
            });

        private WarpFilter m_filter;
        private int m_level;
        private Rect m_relativeRegion;

        private FilterEffect m_effect;
        private JpegRenderer m_jpegRenderer;

        public WarpHandler(WarpEffect effect, int level)
        {
            if (level < 0) level = 0;
            if (level > 10) level = 10;

            m_level = level;
            m_relativeRegion = new Rect(0.0, 0.0, 1.0, 1.0);

            m_filter = new WarpFilter(effect, m_level * 0.1, Rotation.Rotate0, new List<Rect>(new Rect[] { Rect.Empty }));

            m_effect = new FilterEffect()
            {
                Filters = new IFilter[] { m_filter }
            };

            m_jpegRenderer = new JpegRenderer(m_effect);
        }


        public IReadOnlyList<CameraHandlerProperty> Properties
        {
            get { return WarpHandlerProperties; }
        }

        public void SetProperty(Guid guid, object value)
        {
            if (guid == LevelPropertyGuid)
            {
                int level = (int)value;

                if (level < 0) level = 0;
                if (level > 10) level = 10;

                if (m_level != level)
                {
                    // Looks like we need to add thread synchronization here

                    m_level = level;
                    m_filter.Level = m_level * 0.1;
                }
            }
            else
            {
                throw new ArgumentException("No property is associated with this guid.");
            }
        }

        public object GetProperty(Guid guid)
        {
            if (guid == LevelPropertyGuid)
            {
                return m_level;
            }
            else
            {
                throw new ArgumentException("No property is associated with this guid.");
            }
        }

        public Task<Bitmap> GetBitmapImage(IImageProvider source, Bitmap destination, Rotation rotation)
        {
            Task<Bitmap> task = null;

            rotation = ReverseRotation(rotation);
            if (m_filter.Rotation != rotation)
            {
                m_filter.Rotation = rotation;
            }

            if (m_effect.Source != source)
            {
                m_effect.Source = source;

                task = source.GetInfoAsync().AsTask().ContinueWith((t) =>
                    {
                        ImageProviderInfo info = t.Result;

                        m_filter.Regions[0] = new Rect(
                            m_relativeRegion.Left * info.ImageSize.Width,
                            m_relativeRegion.Top * info.ImageSize.Height,
                            m_relativeRegion.Width * info.ImageSize.Width,
                            m_relativeRegion.Height * info.ImageSize.Height
                            );
                    }).ContinueWith((t) => m_effect.GetBitmapAsync(destination, OutputOption.PreserveAspectRatio).AsTask()).Unwrap();
                
            }
            else
            {
                task = m_effect.GetBitmapAsync(destination, OutputOption.PreserveAspectRatio).AsTask();
            }

            return task;
        }

        public Task<IBuffer> GetJpegImage(IImageProvider source, Rotation rotation)
        {
            Task<IBuffer> task = null;

            Rotation reverse = ReverseRotation(rotation);
            if (m_filter.Rotation != reverse)
            {
                m_filter.Rotation = reverse;
            }

            if (m_effect.Source != source)
            {
                m_effect.Source = source;

                task = source.GetInfoAsync().AsTask().ContinueWith((t) =>
                {
                    ImageProviderInfo info = t.Result;

                    m_filter.Regions[0] = new Rect(
                        m_relativeRegion.Left * info.ImageSize.Width,
                        m_relativeRegion.Top * info.ImageSize.Height,
                        m_relativeRegion.Width * info.ImageSize.Width,
                        m_relativeRegion.Height * info.ImageSize.Height
                        );
                }).ContinueWith((t) => m_jpegRenderer.RenderAsync().AsTask()).Unwrap();
            }
            else
            {
                task = m_jpegRenderer.RenderAsync().AsTask();
            }

            if (rotation != Rotation.Rotate0)
            {
                task = task.ContinueWith((t) => JpegTools.FlipAndRotateAsync(t.Result, FlipMode.None, rotation, JpegOperation.AllowLossy).AsTask()).Unwrap();
            }

            return task;
        }

        private Rotation ReverseRotation(Rotation rotation)
        {
            switch (rotation)
            {
                case Rotation.Rotate90:
                    rotation = Rotation.Rotate270;
                    break;
                case Rotation.Rotate270:
                    rotation = Rotation.Rotate90;
                    break;
            }

            return rotation;
        }
    }
}
